package control;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import model.CameraData;
import model.FileVO;
import model.GenericTreeNode;
import model.Vertex3f;

public class CameraManager {

	
	private static CameraData cameraData = new CameraData();
	private static DirectoriesManager dm;
	
	private static long lastTime;
	
	private static Vertex3f startPoint;
	private static Vertex3f endPoint;
	private static Vertex3f przesuniecie = new Vertex3f();
	
	private static boolean inMotion = false;
	
	
	private static long travelTime = 1000; 
	private static float accuracy = 0.01f;
	
	private static float allDistPercentage = 0;
	
	private static List<GenericTreeNode<FileVO>> filesToSee = new ArrayList<GenericTreeNode<FileVO>>();
	
	/**
	 * Dodaje plik (wierzcholek) do kolejki animacji.
	 * @param file
	 */
	public static void addFileToSee(GenericTreeNode<FileVO> file) {
		if (filesToSee.size() == 0) {
			cameraData.getLookAtPosition().set(file.getData().getPosition());
		}
		filesToSee.add(file);
		travelTime = 1000/filesToSee.size();
	}
	
	/**
	 * Ustawia poczatkowy i koncowych punkty animacji. Liczy przesuniecie i zaznacza ze jest wykonywana animacja.
	 * @param s
	 * @param e
	 */
	private static void setCurrentTravelVector(GenericTreeNode<FileVO> s, GenericTreeNode<FileVO> e) {
		startPoint = s.getData().getPosition();
		endPoint = e.getData().getPosition();	
		przesuniecie.x = endPoint.x - startPoint.x;
		przesuniecie.y = endPoint.y - startPoint.y;
		przesuniecie.z = endPoint.z - startPoint.z;
		inMotion = true;
	}
	
	/**
	 * Zwraca dane dotyczace ustawienia kamery.
	 * @return
	 */
	public static CameraData getCameraData() {
		
		//sprawdza ile minelo od ostatniego wywolania tej metody.
		long now = (new Date()).getTime();
		long interval = now - lastTime;
		lastTime = now;
		
		//jesli jest jeden plik na ktory chcemy patrzec to kamera sie nie porusza.
		if (filesToSee.size() <= 1) {
			return cameraData;
		}
		
		//jesli jest wiecej plikow na ktore chcemy patrzec (2, 3 itd) i nie wykonuje sie animacja
		//to ustawia dane dotyczace pierwszej animacji.
		if (!inMotion) {
			setCurrentTravelVector(filesToSee.get(0), filesToSee.get(1));
		}
		
		//sprawdza czy juz 'dotarlismy na miejsce' z kamera
		if (areRoughtlyEqual(cameraData.getLookAtPosition(), endPoint)) {
			cameraData.setLookAtPosition(endPoint);
			inMotion = false;
			filesToSee.remove(0);
			allDistPercentage = 0;
			return getCameraData();
		}
	
		//bierze pod uwage ile czasu mienlo od ostatniego zwrocenia pozycji kamery
		//i zapisuje ile % dystansu powinno byc teraz pokonane
		float distPercentage = (float) interval/travelTime;
		if (distPercentage > (1 - allDistPercentage)) {
			distPercentage = 1 - allDistPercentage;
		}
		allDistPercentage += distPercentage;
			
		
		Vertex3f newLookAt = new Vertex3f(przesuniecie.x, przesuniecie.y, przesuniecie.z);  //cale przesuniecie
		newLookAt.mult(distPercentage);														//przesuniecie w tym kroku
		newLookAt.add(cameraData.getLookAtPosition());										//przesuniecie + pkt przed przesunieciem = pkt po przesunieciu
		cameraData.setLookAtPosition(newLookAt);											//ustawienie nowego punktu
		cameraData.setEyePosition(dm.getCurrentDirectory().get(0).getData().getParentCircleCenter());
		
		return cameraData;
	}
	
	/**
	 * Sprawdza czy punkty w przestrzeni sa mniej wiecej rowne.
	 * @param a
	 * @param b
	 * @return
	 */
	private static boolean areRoughtlyEqual(Vertex3f a, Vertex3f b) {		
		float diffX = Math.abs(a.x - b.x);
		float diffY = Math.abs(a.y - b.y);
		float diffZ = Math.abs(a.z - b.z);	
		return (diffX < accuracy && diffY < accuracy && diffZ < accuracy);	
	}
	
	
	public static void setDirectoriesManager(DirectoriesManager dm) {
		CameraManager.dm = dm;
	}
	
}
